from datetime import datetime
from typing import Optional
import hashlib
import secrets

from loguru import logger
from sqlalchemy import Column, String, DateTime, Boolean, Text
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import select

Base = declarative_base()


class User(Base):
    """用户表"""
    __tablename__ = 'users'

    username = Column(String(50), primary_key=True, index=True)
    password_hash = Column(String(128), nullable=False)
    email = Column(String(100), unique=True, index=True, nullable=True)
    full_name = Column(String(100), nullable=True)
    is_active = Column(Boolean, default=True, nullable=False)
    is_superuser = Column(Boolean, default=False, nullable=False)
    created_at = Column(DateTime, default=datetime.now, nullable=False)
    last_login = Column(DateTime, nullable=True)
    avatar_url = Column(String(500), nullable=True)
    bio = Column(Text, nullable=True)

    @staticmethod
    def hash_password(password: str) -> str:
        """哈希密码"""
        salt = secrets.token_hex(16)
        pwd_hash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt.encode('utf-8'), 100000)
        return f"{salt}:{pwd_hash.hex()}"

    def verify_password(self, password: str) -> bool:
        """验证密码"""
        try:
            salt, stored_hash = self.password_hash.split(':')
            pwd_hash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt.encode('utf-8'), 100000)
            return pwd_hash.hex() == stored_hash
        except ValueError:
            return False

    def to_dict(self) -> dict:
        """转换为字典"""
        return {
            'username': self.username,
            'email': self.email,
            'full_name': self.full_name,
            'is_active': self.is_active,
            'is_superuser': self.is_superuser,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'last_login': self.last_login.isoformat() if self.last_login else None,
            'avatar_url': self.avatar_url,
            'bio': self.bio
        }


class UserSession(Base):
    """用户会话表"""
    __tablename__ = 'user_sessions'

    session_token = Column(String(128), primary_key=True, index=True)
    username = Column(String(50), nullable=False, index=True)
    created_at = Column(DateTime, default=datetime.now, nullable=False)
    expires_at = Column(DateTime, nullable=False)
    is_active = Column(Boolean, default=True, nullable=False)
    user_agent = Column(String(500), nullable=True)
    ip_address = Column(String(45), nullable=True)

    def is_expired(self) -> bool:
        """检查会话是否过期"""
        return datetime.now() > self.expires_at

    def to_dict(self) -> dict:
        """转换为字典"""
        return {
            'session_token': self.session_token,
            'username': self.username,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'expires_at': self.expires_at.isoformat() if self.expires_at else None,
            'is_active': self.is_active,
            'user_agent': self.user_agent,
            'ip_address': self.ip_address
        }


class UserManager:
    """用户管理器"""

    def __init__(self):
        self.engine = None

    async def _get_engine(self):
        """获取数据库引擎"""
        if self.engine is None:
            from ai_platform.config.resource import get_db_engine
            self.engine = get_db_engine()
        return self.engine

    async def init_tables(self):
        """初始化数据库表"""
        try:
            engine = await self._get_engine()
            async with engine.begin() as conn:
                await conn.run_sync(Base.metadata.create_all)

            # 创建默认管理员用户
            await self.create_default_admin()
            logger.info("用户表初始化完成")
        except Exception as e:
            logger.exception(f"用户表初始化失败: {e}")
            raise

    async def create_default_admin(self):
        """创建默认管理员用户"""
        try:
            admin_exists = await self.get_user("admin")
            if not admin_exists:
                await self.create_user(
                    username="admin",
                    password="admin123",
                    email="admin@example.com",
                    full_name="系统管理员",
                    is_superuser=True
                )
                logger.info("创建默认管理员用户: admin/admin123")
        except Exception as e:
            logger.exception(f"创建默认管理员用户失败: {e}")

    async def create_user(self, username: str, password: str, email: str = None,
        full_name: str = None, is_superuser: bool = False) -> Optional[User]:
        """创建新用户"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                # 检查用户是否已存在
                existing_user = await session.get(User, username)
                if existing_user:
                    logger.warning(f"用户 {username} 已存在")
                    return None

                # 检查邮箱是否已存在
                if email:
                    stmt = select(User).where(User.email == email)
                    result = await session.execute(stmt)
                    if result.scalar_one_or_none():
                        logger.warning(f"邮箱 {email} 已被使用")
                        return None

                # 创建新用户
                new_user = User(
                    username=username,
                    password_hash=User.hash_password(password),
                    email=email,
                    full_name=full_name,
                    is_superuser=is_superuser,
                    created_at=datetime.now()
                )

                session.add(new_user)
                await session.commit()
                await session.refresh(new_user)
                logger.info(f"创建新用户: {username}")
                return new_user

        except Exception as e:
            logger.exception(f"创建用户失败: {e}")
            return None

    async def authenticate_user(self, username: str, password: str) -> Optional[User]:
        """验证用户登录"""
        try:
            user = await self.get_user(username)
            if user and user.is_active and user.verify_password(password):
                # 更新最后登录时间
                await self.update_last_login(username)
                return user
            return None
        except Exception as e:
            logger.exception(f"用户验证失败: {e}")
            return None

    async def get_user(self, username: str) -> Optional[User]:
        """获取用户信息"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                return await session.get(User, username)
        except Exception as e:
            logger.exception(f"获取用户失败: {e}")
            return None

    async def update_last_login(self, username: str) -> bool:
        """更新最后登录时间"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                user = await session.get(User, username)
                if user:
                    user.last_login = datetime.now()
                    await session.commit()
                    return True
                return False
        except Exception as e:
            logger.exception(f"更新最后登录时间失败: {e}")
            return False

    async def create_session(self, username: str, expires_hours: int = 24,
        user_agent: str = None, ip_address: str = None) -> Optional[UserSession]:
        """创建用户会话"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                # 生成会话令牌
                session_token = secrets.token_urlsafe(64)
                from datetime import timedelta
                expires_at = datetime.now().replace(microsecond=0) + \
                             timedelta(hours=expires_hours)

                new_session = UserSession(
                    session_token=session_token,
                    username=username,
                    created_at=datetime.now(),
                    expires_at=expires_at,
                    user_agent=user_agent,
                    ip_address=ip_address
                )

                session.add(new_session)
                await session.commit()
                await session.refresh(new_session)
                logger.info(f"为用户 {username} 创建会话")
                return new_session

        except Exception as e:
            logger.exception(f"创建用户会话失败: {e}")
            return None

    async def get_session(self, session_token: str) -> Optional[UserSession]:
        """获取用户会话"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                user_session = await session.get(UserSession, session_token)
                if user_session and not user_session.is_expired() and user_session.is_active:
                    return user_session
                return None
        except Exception as e:
            logger.exception(f"获取用户会话失败: {e}")
            return None

    async def invalidate_session(self, session_token: str) -> bool:
        """使会话失效"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                user_session = await session.get(UserSession, session_token)
                if user_session:
                    user_session.is_active = False
                    await session.commit()
                    return True
                return False
        except Exception as e:
            logger.exception(f"使会话失效失败: {e}")
            return False

    async def cleanup_expired_sessions(self) -> int:
        """清理过期会话"""
        try:
            engine = await self._get_engine()
            async with AsyncSession(engine) as session:
                from sqlalchemy import delete
                stmt = delete(UserSession).where(UserSession.expires_at < datetime.now())
                result = await session.execute(stmt)
                await session.commit()
                count = result.rowcount
                logger.info(f"清理了 {count} 个过期会话")
                return count
        except Exception as e:
            logger.exception(f"清理过期会话失败: {e}")
            return 0


# 全局用户管理器实例
user_manager = UserManager()
